package me.yidasanqian.crowdfunding.app;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.StringUtils;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import me.yidasanqian.crowdfunding.domain.*;
import me.yidasanqian.crowdfunding.service.ICrowdFundingService;
import me.yidasanqian.crowdfunding.service.IOrderService;
import me.yidasanqian.crowdfunding.vo.ResultModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Linyu Chen
 */
@RestController
@RequestMapping("api/v1/alipay")
public class AlipayApi {
    private static final String SERVER_URL = "https://openapi.alipay.com/gateway.do";
    private static final String APP_ID = "2017072507886220";
    private static final String APP_PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjFWtX7PWYWRxB2hfRF7ffeIPYyjG33rNQB+7iIU+3ek0ANjjpOpBJrcmVcGNO0uzWpO13g4wF69tBrXYNXSFdkLN82Xxt7H/NX5K4FZlZwsodHoOwXEsmBPtawEud1j+VE7/q5uhk6F/KM4czS1iFSTX/X4pRFcGD9VjdTsxYX+pV6OCdtPllXyqiaS7QoX6rUi43TF4gQHrFk4Urdzso7ntu80q7Q+c0O3eKKoWaChOmePY5dLIRrA7EtgMUwmUdTLOyKezBsnwZToFVefPKU6tp5tvacpTRyFd4fGsKs4lrE1jzrj4qvKwEgPsPn3c9CZ2CwlgvQcsgcZ5BMfZlAgMBAAECggEAEkrZwoACwDbmk4BaAYsOhdNnvo5Xa09gf46UPesMnQDr2CU81vRJsOn8XXXAvdus0xqmxCwOLa/Z5REG19LlZjBP4aYnbdEEOM4Kj/lkPQw7TW7ZyJNhnn8ajoBZOvgII7mWYkvKL19dNzn2ZUuDP1gJ5X4a7qNpFFsVv6USCKtzq498rXLPHbwWrSztZFOR4dEiSA9S85JIxLJ/Kwdq8+eU+nt4j/nMhfmcA+yicczUNwS5dLz1hlSk/VoWXBcSa2AhjYRi87s9iN9isYQ10a+D4UfkK1qvQafP4m23pqElMA8UA2iethN/uT5CZo+VlgLi1gNYTO4i7fes1A0rtQKBgQDSDIVj3tUhc0iDc0U/PvS3U80NPM8wTNOuf01Yv1utK7u5Fc30kKibsu+JvxBG76N6MyHP0AQJjdCPpRsTyliwAfIyw287NLyIyu7b1ovVEfi6j0sqwfGd1LIhuZBjSNmbsIo+rbdpE8g4mgbUH+3ebIO0/N5q9188wDTX5WDGQwKBgQDGwq3mgxAFngxUmrqsoE2GRmKu61GlqbskjpjImFCy0qh47zoOV2EVrFX+Yurwd8pNrR4XAMah7ErSUisw01Se1oUQAbYLo6j6O4BK9Ce1jTrhyB+7tXdMBRgbRS/d9HWahi8cn9xcblLwRfI0+iROVmjVprlk5yUcqwVXGghKNwKBgD+ycFkC4BsOvLSwJhWx38Agd5RGFoaNJbxK+oepMAHpuzm7dQNvQtJEhwN8n1EDps2N5/j/GdpT2SVWakmsGQz26h2eyoJn0ayh7aFZBHqOwqDAh4jl24GDWKmFflW9Z9AgzyuGvpHs39DaVKvLKvVCbwZZkJFbhtQDbYLzGk/FAoGAAWOW54d+8ysZlvXF3DL6l7eiNOq8FVZMUoMUmkJGPhmux7mURElkCky8BR0X2zpbFZPiI/MDcFpAY5vjZWH0s294WpZIeWhZY/ZKfTubk287PByz7BHmlYdrlbU0Em8E62GIrsCHP7YdqkQy3x/oZQvBi8ywev4Kilq/nM43xJ0CgYEApzhv0vUJaybTJ2WFLaVTq/8JAwQY+ThiGwxKHxFR+whjkkTluFtbSVW7PDgcprwWOxZ74IXYtCbPVTdwteJ2zA95z9DVeLOvozCPNdROUEvdL2OmY9yM89SOcG4tZBJMvLx/fykzQYYuOYubedtS/ucMAGMEXrLl3Dg5vAaM3fQ=";
    private static final String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApwVnzNayjtAcEwgpP2J9SkSw1CYnU2qojOn5xHenQl6uGa13hN/A9KmIOMkwEtvX5DF3jIZ5h0j+VT1ugPpjfjc5p1K2kWZumKaqBDjemp3ppt2Ia0+tXcVjJruMPTPGgRcReuAXmROE1VcYqZHTZMYdbC8WSAIboAinXsHORF8WXgUYE9EOQRzRgC+KtP47MgLSRlMjHcA3tjt9eSoxTGSXlxlwekdc003vKh/EwkkFbDEW+MgQnrr5atEG2aUMObFYQvZCUpwqdt4cGkTJTpQ9dtDC5z0GurdpQ2ASOdDTAbKNNdVuVIw8ct5bI9bgC5D480shcbuZK7d5nqKXAQIDAQAB";
    private static final String CHARSET = "UTF-8";
    private static final String SIGN_TYPE = "RSA2";
    private static final String FORMAT = "json";

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Resource
    private ICrowdFundingService crowdFundingService;

    @Resource
    private IOrderService orderService;

    @Value("${crowdfunding.alipay.notify_url}")
    private String NOTIFY_URL;
    @Value("${crowdfunding.alipay.return_url}")
    private String RETURN_URL;

    /**
     * 生成APP支付订单信息
     *
     * @param requestParams 业务参数
     */
    @RequestMapping(value = "signature", method = RequestMethod.POST)
    public Object signature(@RequestBody String requestParams) {
        try {
            log.info("请求体requestParams ==> " + requestParams);
            String deBiz = URLDecoder.decode(requestParams, "utf-8");
            log.info("解码后的请求体deBiz ==> " + deBiz);

            Gson gson = new Gson();
            BizContent bizContent = gson.fromJson(requestParams, BizContent.class);

            int number;
            String body, subject, outTradeNo;
            BigDecimal totalAmount;
            Long userId, fundId, rewardId;

            fundId = bizContent.getFundId();
            userId = bizContent.getUserId();
            number = bizContent.getNumber();
            totalAmount = bizContent.getTotalAmount();
            outTradeNo = bizContent.getOutTradeNo();
            rewardId = bizContent.getRewardId();

            if (StringUtils.isEmpty(outTradeNo)) {
                return ResultModel.fail(1017, "订单号不能为空");
            }

            Order order = new Order();
            //order.setCreatetime(new Date());
            order.setFundId(fundId);
            order.setUserId(userId);
            order.setNumber(number);
            order.setAmount(totalAmount);
            order.setOutTradeNo(outTradeNo);
            order.setRewardId(rewardId);
            order.setStatus(1);

            // 实例化客户端
            AlipayClient alipayClient = new DefaultAlipayClient(SERVER_URL, APP_ID, APP_PRIVATE_KEY, FORMAT,
                    CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);
            // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            //AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
            // SDK已经封装掉了公共参数，这里只需要传入业务参数。
            request.setBizContent(requestParams);
            request.setNotifyUrl(NOTIFY_URL);
            request.setReturnUrl(RETURN_URL);
            // 这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            // 调用成功，则处理业务逻辑
            if (response.isSuccess()) {
                // 保存未付款的订单信息
                orderService.save(order);

                // 处理订单签名信息
                String orderString = response.getBody();
                int beginIndex = orderString.indexOf("&") + 1;
                String orderSign = orderString.substring(beginIndex, orderString.length());
                Map<String, String> map = new LinkedHashMap<String, String>();
                log.info("处理后的签名orderSign ==> " + orderSign);
                map.put("orderString", orderSign);
                return ResultModel.success(map);
            }
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
            return ResultModel.fail(1014, "创建订单失败");
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
            return ResultModel.fail(1014, "创建订单失败");
        }
        return ResultModel.fail(1014, "创建订单失败");
    }

    /**
     * 手机网站支付
     * https://docs.open.alipay.com/203/105285/
     *
     * @param requestParams
     * @param response
     */
    @RequestMapping(value = "generateOrderForm", method = RequestMethod.POST)
    public void generateOrderForm(@RequestBody String requestParams,
                                  HttpServletResponse response, PrintWriter writer) {

        Gson gson = new Gson();
        BizContent bizContent = gson.fromJson(requestParams, BizContent.class);

        int number;
        String body, subject, outTradeNo, timeoutExpress, productCode;
        BigDecimal totalAmount;
        Long userId, fundId, rewardId;

        body = bizContent.getBody();
        subject = bizContent.getSubject();
        timeoutExpress = bizContent.getTimeoutExpress();
        productCode = bizContent.getProductCode();

        fundId = bizContent.getFundId();
        userId = bizContent.getUserId();
        number = bizContent.getNumber();
        totalAmount = bizContent.getTotalAmount();
        outTradeNo = bizContent.getOutTradeNo();
        rewardId = bizContent.getRewardId();

        CrowdFund crowdFund = crowdFundingService.getCrowdFund(fundId);
        Progress progress = crowdFund.getProgress();
        // 处理支持人数,每人只能支持众筹项目下的一个回报
        String supportPeople = progress.getSurportPeople();
        if (!StringUtils.isEmpty(supportPeople)) {
            String[] supportPeopleArr = supportPeople.split("\\|");
            for (int i = 0; i < supportPeopleArr.length; i++) {
                String sp = supportPeopleArr[i];
                if (!StringUtils.isEmpty(sp) && sp.equals(String.valueOf(userId))) {
                    response.setContentType("application/json;charset=" + CHARSET);
                    JsonObject jsonObject = new JsonObject();
                    jsonObject.addProperty("code", 1018);
                    jsonObject.addProperty("message", "您已支持过该众筹项目");
                    writer.write(jsonObject.toString());
                    writer.flush();
                    writer.close();
                    return;
                }
            }
        }

        Order order = new Order();
        //order.setCreatetime(new Date());
        order.setFundId(fundId);
        order.setUserId(userId);
        order.setNumber(number);
        order.setAmount(totalAmount);
        order.setOutTradeNo(outTradeNo);
        order.setRewardId(rewardId);
        order.setStatus(1);
        // 保存未付款的订单信息
        orderService.save(order);

        AlipayClient alipayClient = new DefaultAlipayClient(SERVER_URL, APP_ID, APP_PRIVATE_KEY, FORMAT,
                CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);
        // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
        // SDK已经封装掉了公共参数，这里只需要传入业务参数。
        // 封装请求支付信息
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo(outTradeNo);
        model.setSubject(subject);
        model.setTotalAmount(String.valueOf(totalAmount));
        model.setBody(body);
        model.setTimeoutExpress(timeoutExpress);
        model.setProductCode(productCode);
        alipayRequest.setBizModel(model);
        alipayRequest.setNotifyUrl(NOTIFY_URL);
        //alipayRequest.setReturnUrl(RETURN_URL);

        // form表单生产
        String form = "";
        try {
            // 调用SDK生成表单
            form = alipayClient.pageExecute(alipayRequest).getBody();
            response.setContentType("text/html;charset=" + CHARSET);
            log.info(form);
            writer.write(form);//直接将完整的表单html输出到页面
            writer.flush();
            writer.close();
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 对于App支付产生的交易，支付宝会根据原始支付API中传入的异步通知地址notify_url，
     * 通过POST请求的形式将支付结果作为参数通知到商户系统
     *
     * @param request
     * @see <a href=https://docs.open.alipay.com/204/105301/>
     * https://docs.open.alipay.com/204/105301</a>
     */
    @RequestMapping(value = "checkAsyncNotify", method = RequestMethod.POST)
    public void checkAsyncNotify(HttpServletRequest request, HttpServletResponse response, PrintWriter writer) {
        try {
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<String, String>();
            Map requestParams = request.getParameterMap();
            for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                String name = (String) iter.next();
                String[] values = (String[]) requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                }
                // 乱码解决，这段代码在出现乱码时使用。
                // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }

            // 商户订单号
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
            // 支付宝交易号
            String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
            // 交易状态
            String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

            log.info("异步通知 outTradeNo <== " + outTradeNo + "\ttradeNo <== " + tradeNo + "\ttradeStatus <== " + tradeStatus);

            // 切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
            boolean signVerfied = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, CHARSET, SIGN_TYPE);
            if (signVerfied) {  // 验签成功
                // 请在这里加上商户的业务逻辑程序代码
                // 从订单中获取支付订单信息，如果存在支付订单信息则更新，否则新建支付订单
                Order order = orderService.getByOutTradeNo(outTradeNo);
                PayOrder payOrder = new PayOrder();
                //payOrder.setCreatetime(new Date());
                payOrder.setOrderChannel(1);
                if (order.getStatus() == 1) {
                    order.setStatus(2);
                    payOrder.setStatus(2);
                }
                //payOrder.setCreatetime(new Date());
                payOrder.setOrderId(order.getId());

                Long fundId = order.getFundId();
                Long userId = order.getUserId();
                BigDecimal amount = order.getAmount();

                CrowdFund crowdFund = crowdFundingService.getCrowdFund(fundId);
                Progress progress = crowdFund.getProgress();
                // 处理支持人数
                String supportPeople = progress.getSurportPeople();
                if (StringUtils.isEmpty(supportPeople)) {
                    supportPeople = "";
                }
                supportPeople += userId;
                supportPeople += "|";
                progress.setSurportPeople(supportPeople);
                // 处理已筹金额
                BigDecimal hasFund = progress.getHasFund();
                if (hasFund == null) {
                    hasFund = BigDecimal.valueOf(0);
                }
                hasFund = hasFund.add(amount);
                progress.setHasFund(hasFund);
                //progress.setUpdateTime(new Date());
                crowdFund.setProgress(progress);
                crowdFundingService.updateProgress(progress);
                crowdFundingService.save(crowdFund);

                orderService.updateOrder(order);
                orderService.savePay(payOrder);

                // ——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
                //if(tradeStatus.equals("TRADE_FINISHED")){
                // 判断该笔订单是否在商户网站中已经做过处理
                // 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                // 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                // 如果有做过处理，不执行商户的业务程序

                // 注意：
                // 如果签约的是可退款协议，退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
                // 如果没有签约可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
                //} else if (tradeStatus.equals("TRADE_SUCCESS")){
                // 判断该笔订单是否在商户网站中已经做过处理
                // 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                // 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                // 如果有做过处理，不执行商户的业务程序

                // 注意：
                // 如果签约的是可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
                // }
                // https://docs.open.alipay.com/204/105301/
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，
                // 校验成功后在response中返回success，校验失败返回failure
                writer.write("success");
                writer.flush();
                writer.close();
            } else {
                // 验签失败则记录异常日志，并在response中返回failure.
                log.warn("异步通知 验签失败");
                writer.write("failure");
                writer.flush();
                writer.close();
            }
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 手机网站支付同步通知验证
     *
     *
     * @param request
     * @param response
     */
    @RequestMapping(value = "checkSyncNotify", method = RequestMethod.GET)
    public void checkSyncNotify(HttpServletRequest request, HttpServletResponse response, PrintWriter writer) {
        // 获取支付宝同步GET过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            // 乱码解决，这段代码在出现乱码时使用。
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }

        try {
            // 切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
            boolean signVerfied = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, CHARSET, SIGN_TYPE);
            if (signVerfied) {
                String servletPath = request.getScheme() + "://" + request.getServerName() +
                        ":" + request.getServerPort();
                String returnView = servletPath + "/pay/return_url.html";
                log.info("同步通知 验签成功returnView <== " + returnView);
                response.sendRedirect(returnView);
              /*  writer.write("<html>");
                writer.write("<body>");
                writer.write("验证成功<br/>");
                writer.write("</body>");
                writer.write("</html>");

                writer.flush();
                writer.close();*/

            } else {
                // 验签失败则记录异常日志，并在response中返回failure.
                log.warn("同步通知 验签失败");
               /* writer.write("<html>");
                writer.write("<body>");
                writer.write("验证失败");
                writer.write("</body>");
                writer.write("</html>");

                writer.flush();
                writer.close();*/

            }
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }
}
